﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Interfaces;
using SoftwareConsulting.BI11.NumericalMethods.DhbFunctionEvaluation;

namespace SoftwareConsulting.BI11.NumericalMethods.Iterations
{    
    /// <summary>
    /// Класс незавершенной бета-функции
    /// </summary>
    public class IncompleteBetaFunction : IOneVariableFunction
    {	    
        private double _alpha1;
        private double _alpha2;	    
        private double _logNorm;//вычисляет только 1 раз	    
        private IncompleteBetaFunctionFraction _fraction; //цепные дроби        

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="alpha1"></param>
        /// <param name="alpha2"></param>
        public IncompleteBetaFunction(double alpha1, double alpha2)
        {
            _alpha1 = alpha1;
            _alpha2 = alpha2;
            _logNorm = GammaFunction.LogGamma(_alpha1 + _alpha2) - GammaFunction.LogGamma(_alpha1) - GammaFunction.LogGamma(_alpha2);
        }

        /// <summary>
        /// Вычисляет дробь для указанного аргумента
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private double GetFraction(double x)
        {
            if (_fraction == null)
            {
                _fraction = new IncompleteBetaFunctionFraction(_alpha1, _alpha2);
                _fraction.SetDesiredPrecision(MathExtended.DefaultNumericalPrecision);
            }
            _fraction.Argument = x;
            _fraction.Evaluate();
            return _fraction.Result;
        }

        /// <summary>
        /// Возвращает дробь обратной функции для указанного аргумента
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private double GetInversedFraction(double x)
        {
            if (_fraction == null)
            {
                _fraction = new IncompleteBetaFunctionFraction(_alpha2, _alpha1);
                _fraction.SetDesiredPrecision(MathExtended.DefaultNumericalPrecision);
            }
            _fraction.Argument = x;
            _fraction.Evaluate();
            return _fraction.Result;
        }

        /// <summary>
        /// Возвращает значение функции для указанного аргумента
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public double GetValue(double x)
        {
            if (x == 0)
                return 0;
            if (x == 1)
                return 1;
            double norm = Math.Exp(_alpha1 * Math.Log(x)
                                        + _alpha2 * Math.Log(1 - x) + _logNorm);
            return (_alpha1 + _alpha2 + 2) * x < (_alpha1 + 1)
                            ? norm / (GetFraction(x) * _alpha1)
                            : 1 - norm / (GetInversedFraction(1 - x)
                                                                    * _alpha2);
        }
    }
}
